/*
 * The contents of this web application are subject to the Mozilla Public License Version 
 * 1.1 (the "License"); you may not use this web application except in compliance with 
 * the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 * for the specific language governing rights and limitations under the License.
 * 
 * The Original Code is owned by and the Initial Developer of the Original Code is 
 * Composite A/S (Danish business reg.no. 21744409). All Rights Reserved
 * 
 * Section 11 of the License is EXPRESSLY amended to include a provision stating 
 * that any dispute, including but not limited to disputes related to the enforcement 
 * of the License, to which Composite A/S as owner of the Original Code, as Initial 
 * Developer or in any other role, becomes a part to shall be governed by Danish law 
 * and be initiated before the Copenhagen City Court ("K�benhavns Byret")            
 */

using System;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using Composite.Core.Extensions;
using Composite.Core.ResourceSystem;

namespace Composite.Core.WebClient.UiControlLib
{
    /// <summary>
    /// To be used for creating stateless tags. Copies all the attributes from markup to the generated tag,
    /// the "clientid" attribute will be transformed to "id" attribute.
    /// </summary>
    /// <exclude />    
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] 
    public class Generic : HtmlGenericControl 
    {
        private string _callbackid;
        private string _methodName;
        private bool? _posted;


        /// <exclude />
        public Generic(string tag)
            : base(tag)
        {
            base.EnableViewState = false;
        }


        /// <exclude />
        public virtual bool IsPosted
        {
            get
            {
                Verify.IsNotNull(_posted, "This property isn't awailable until 'PageLoad' event");
                return _posted.Value;
            }
        }


        /// <exclude />
        protected override void OnLoad(System.EventArgs e)
        {
            _posted = false;
            base.OnLoad(e);

            if (Attributes["HasCallbackId"] == "true" && Attributes["callbackid"].IsNullOrEmpty())
            {
                Attributes.Remove("HasCallbackId");
                Attributes["callbackid"] = this.UniqueID;
            }

            _callbackid = this.Attributes["callbackid"] ?? string.Empty;
            _methodName = Attributes["OnCommand"];

            // NOTE: to be removed
            if(_methodName.IsNullOrEmpty())
            {
                _methodName = Attributes["OnServerClick"];
            }

            if(_callbackid != null
                && Page.IsPostBack 
                && !Page.Request.Form["__EVENTTARGET"].IsNullOrEmpty()
                && Page.Request.Form["__EVENTTARGET"].Replace('$', '_') == _callbackid.Replace('$', '_'))
            {
                _posted = true;
                if(_methodName != null)
                {
                    this.Page.RegisterRequiresRaiseEvent(new PostBackEventHandler(this));
                }
            }
        }



        /// <exclude />
        protected override void RenderAttributes(HtmlTextWriter writer)
        {
            string clientId = Attributes["clientid"];
            if(clientId.IsNullOrEmpty() && !ID.IsNullOrEmpty())
            {
                clientId = ID;
            }

            if (!clientId.IsNullOrEmpty())
            {
                writer.WriteAttribute("id", clientId);

                Attributes.Remove("clientid");
            }

            string label = Attributes["label"];
            if (!label.IsNullOrEmpty())
            {
                Attributes["label"] = StringResourceSystemFacade.ParseString(label);
            }

            Attributes.Remove("OnServerClick"); // Server-side attribute
            Attributes.Remove("OnCommand"); // Server-side attribute

            this.Attributes.Render(writer);
        }



        /// <exclude />
        protected class PostBackEventHandler : IPostBackEventHandler
        {
            private Generic _control;


            /// <exclude />
            public PostBackEventHandler(Generic control)
            {
                _control = control;
            }


            /// <exclude />
            public void RaisePostBackEvent(string eventArgument)
            {
                string methodName = _control._methodName;

                Control controlThatHandlesEvent = GetControlThatHandlesEvent(_control);

                if (controlThatHandlesEvent == null)
                {
                    Core.Logging.LoggingService.LogError(typeof(Generic).FullName, "Failed to find parent control, that appropriate for event handling".FormatWith(methodName));
                    return;
                }

                Type type = controlThatHandlesEvent.GetType();

                MethodInfo methodInfo = null;
                
                while(methodInfo == null && type != typeof(object))
                {
                    methodInfo = type.GetMethod(methodName);
                    type = type.BaseType;
                }

                if(methodInfo == null)
                {
                    Log.LogError(typeof(Generic).FullName, "Failed to find method '{0}'", methodName);
                    return;
                }

                methodInfo.Invoke(controlThatHandlesEvent, new object[0]);
            }


            private static Control GetControlThatHandlesEvent(Generic generic)
            {
                Control control = generic;
                do
                {
                    control = control.Parent;
                } while (control != null 
                    && !(control is UserControl)
                    && !(control is Page));

                return control;
            }
        }
    }
}